/**
 * Copyright 2018 orivil.com. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found at https://mit-license.org.
 */

package session

import (
	"net/rpc"
	"net"
	"gitee.com/tugoer/orivil/log"
	"os"
	"net/http"
	"time"
	"sync"
	"gitee.com/tugoer/orivil/timer"
)

type sessionValue struct {
	id string
	activeAt *time.Time
	values map[string]string
	load bool
}

type RpcContainer struct {
	sessions map[string]*sessionValue
	locker sync.Mutex
	sectionTime *timer.SectionProvider
	env *env
	storage Storage
}

//var RpcContainer  = NewRpcContainer()

func NewRpcContainer() (rc *RpcContainer) {
	rc = &RpcContainer {}
	rc.loadEnv(Env)
	return rc
}

func (rc *RpcContainer) setStorage(storage Storage) error {
	rc.locker.Lock()
	defer rc.locker.Unlock()
	ids, err := storage.ReadAllIDs()
	if err != nil {
		return err
	} else {
		sessions := make(map[string]*sessionValue, len(ids))
		now := rc.sectionTime.Section()
		for _, id := range ids {
			sessions[id] = &sessionValue{id: id, activeAt: now, load:false}
		}
		rc.sessions = sessions
		rc.storage = storage
		return nil
	}
}

func SetContainerStorage(container *RpcContainer, storage Storage) error {
	return container.setStorage(storage)
}

// 加载配置数据
func (rc *RpcContainer) loadEnv(e *env) {
	rc.locker.Lock()
	defer rc.locker.Unlock()
	rc.env = e
	if rc.sectionTime != nil {
		rc.sectionTime.Close()
	}
	rc.sectionTime = timer.NewSectionProvider(e.Expire())
}

func LoadContainerEnv(container *RpcContainer, env *env) {
	container.loadEnv(env)
}

func (rc *RpcContainer) GetValues(id string, replay *map[string]string) (err error) {
	rc.locker.Lock()
	defer rc.locker.Unlock()
	rs := rc.sessions[id]
	if rs == nil {
		rs = &sessionValue{id:id, values:make(map[string]string, 2), load:true}
		rc.sessions[id] = rs
	} else if !rs.load { // 读取未加载的数据
		data, err := rc.storage.ReadSession(id)
		if err != nil {
			return err
		} else {
			if len(data) > 0 {
				values, err := Unmarshal(data)
				if err != nil {
					return err
				} else {
					rs.values = values
					rs.load = true
				}
			} else {
				rs.values = make(map[string]string, 2)
				rs.load = true
			}
		}
	}
	// 设置激活时间
	rs.activeAt = rc.sectionTime.Section()

	// 删除已过期数据
	checkExpires := rc.env.CheckExpireNumEachUpdate
	// 必须在两个时间段之后, 否则可能出现前一时间段刚刚设置会话, 后一时间段马上就被删除的情况
	expireTime := rs.activeAt.Add(-rc.env.Expire())
	for key, session := range rc.sessions {
		if checkExpires > 0 {
			if !session.activeAt.After(expireTime) {
				rc.storage.DelSession(session.id)
				delete(rc.sessions, key)
			}
			checkExpires--
		} else {
			break
		}
	}
	*replay = rs.values
	return nil
}

type SaveArgs struct {
	ID string
	Values map[string]string
}

func (rc *RpcContainer) SaveValues(args *SaveArgs, replay *struct{}) (err error) {
	rc.locker.Lock()
	defer rc.locker.Unlock()
	if len(args.Values) > 0 {
		data, err := Marshal(args.Values)
		if err != nil {
			return err
		} else {
			rc.sessions[args.ID].values = args.Values
			return rc.storage.SaveSession(args.ID, data)
		}
	} else {
		// 删除会话
		delete(rc.sessions, args.ID)
		rc.storage.DelSession(args.ID)
		return nil
	}
}

// 运行 rpc 服务器
func RunServer(container *RpcContainer, address string) error {
	server := rpc.NewServer()
	server.Register(container)
	serveMux := http.NewServeMux()
	serveMux.Handle(rpc.DefaultRPCPath, server)
	l, e := net.Listen("tcp", address)
	if e != nil {
		return e
	}
	log.Init.Printf("pid:[%d] listen on: [%s]\n", os.Getpid(), address)
	return http.Serve(l, serveMux)
}